//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.eResearch.WFServiceLibrary.SchedulerLogic
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using V1 = TaskScheduler.V1;
    using V2 = TaskScheduler;

    /// <summary>
    /// Collection of triggers.
    /// </summary>
    public sealed class TriggerCollection : IEnumerable<Trigger>, IDisposable
    {
        private V1.ITask v1TaskDefinition = null;

        /// <summary>
        /// 
        /// </summary>
        private V2.ITaskDefinition v2TaskDefinition = null;

        /// <summary>
        /// 
        /// </summary>
        private V2.ITriggerCollection triggerCollection = null;

        /// <summary>
        /// Gets the number of triggers in the collection.
        /// </summary>
        public int Count
        {
            get
            {
                int triggerCount = 0;
                if (this.triggerCollection != null)
                {
                    triggerCount = this.triggerCollection.Count;
                }

                return triggerCount;
            }
        }

        internal TriggerCollection(V1.ITask iTask)
        {
            v1TaskDefinition = iTask;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TriggerCollection"/> class.
        /// </summary>
        /// <param name="iTaskDef">The i task def.</param>
        internal TriggerCollection(V2.ITaskDefinition iTaskDef)
        {
            this.v2TaskDefinition = iTaskDef;
            this.triggerCollection = this.v2TaskDefinition.Triggers;
        }

        /// <summary>
        /// Add an unbound <see cref="Trigger"/> to the task.
        /// </summary>
        /// <param name="unboundTrigger"><see cref="Trigger"/> derivative to add to the task.</param>
        /// <returns>Bound trigger.</returns>
        public Trigger Add(Trigger unboundTrigger)
        {
            if (this.v2TaskDefinition != null)
            {
                unboundTrigger.Bind(this.v2TaskDefinition);
            }
            else
            {
                unboundTrigger.Bind(v1TaskDefinition);
            }

            return unboundTrigger;
        }

        internal void Bind()
        {
            foreach (Trigger t in this)
                t.SetV1TriggerData();
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (this.triggerCollection != null)
            {
                Marshal.ReleaseComObject(this.triggerCollection);
            }

            this.v2TaskDefinition = null;
            v1TaskDefinition = null;
        }

        /// <summary>
        /// Clears all triggers from the task.
        /// </summary>
        public void Clear()
        {
            if (this.triggerCollection != null)
            {
                this.triggerCollection.Clear();
            }
            else
            {
                for (int i = this.Count - 1; i >= 0; i--)
                {
                    this.RemoveAt(i);
                }
            }
        }

        /// <summary>
        /// Removes the trigger at a specified index.
        /// </summary>
        /// <param name="index">Index of trigger to remove.</param>
        /// <exception cref="ArgumentOutOfRangeException">Index out of range.</exception>
        public void RemoveAt(int index)
        {
            if (index >= this.Count)
            {
                throw new ArgumentOutOfRangeException("index", index, "Failed to remove Trigger. Index out of range.");
            }

            if (this.triggerCollection != null)
            {
                this.triggerCollection.Remove(++index);
            }
            else
            {
                v1TaskDefinition.DeleteTrigger((ushort)index); //Remove the trigger from the Task Scheduler
            }
        }

        /// <summary>
        /// Gets the collection enumerator for this collection.
        /// </summary>
        /// <returns>The <see cref="IEnumerator{T}"/> for this collection.</returns>
        public IEnumerator<Trigger> GetEnumerator()
        {
            IEnumerator<Trigger> taskTriggerEnum;
            if (v1TaskDefinition != null)
            {
                taskTriggerEnum = new V1TaskTriggerEnumerator(this.v1TaskDefinition);
            }
            else
            {
                taskTriggerEnum = new V2TaskTriggerEnumerator(this.triggerCollection);
            }

            return taskTriggerEnum;
        }

        #region IEnumerable<Trigger> Members

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        internal sealed class V1TaskTriggerEnumerator : IEnumerator<Trigger>
        {
            private V1.ITask iTask;
            private short curItem = -1;

            /// <summary>
            /// Initializes a new instance of the <see cref="V1TaskTriggerEnumerator"/> class.
            /// </summary>
            /// <param name="task">The task.</param>
            internal V1TaskTriggerEnumerator(V1.ITask task)
            {
                this.iTask = task;
            }

            /// <summary>
            /// Gets the element in the collection at the current position of the enumerator.
            /// </summary>
            /// <value></value>
            /// <returns>
            /// The element in the collection at the current position of the enumerator.
            /// </returns>
            public Trigger Current
            {
                get
                {
                    return Trigger.CreateTrigger(this.iTask.GetTrigger((ushort)curItem));
                }
            }

            /// <summary>
            /// Releases all resources used by this class.
            /// </summary>
            public void Dispose()
            {
                this.iTask = null;
            }

            /// <summary>
            /// Gets the element in the collection at the current position of the enumerator.
            /// </summary>
            /// <value></value>
            /// <returns>
            /// The element in the collection at the current position of the enumerator.
            /// </returns>
            object System.Collections.IEnumerator.Current
            {
                get { return this.Current; }
            }

            /// <summary>
            /// Advances the enumerator to the next element of the collection.
            /// </summary>
            /// <returns>
            /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
            /// </returns>
            /// <exception cref="T:System.InvalidOperationException">
            /// The collection was modified after the enumerator was created.
            /// </exception>
            public bool MoveNext()
            {
                if (++this.curItem >= iTask.GetTriggerCount())
                    return false;
                return true;
            }

            /// <summary>
            /// Sets the enumerator to its initial position, which is before the first element in the collection.
            /// </summary>
            /// <exception cref="T:System.InvalidOperationException">
            /// The collection was modified after the enumerator was created.
            /// </exception>
            public void Reset()
            {
                this.curItem = -1;
            }
        }

        /// <summary>
        /// Supports a simple iteration over task trigger. 
        /// </summary>
        internal sealed class V2TaskTriggerEnumerator : IEnumerator<Trigger>
        {
            /// <summary>
            /// Newer version enumeration.
            /// </summary>
            private System.Collections.IEnumerator iEnum;

            /// <summary>
            /// Initializes a new instance of the <see cref="V2TaskTriggerEnumerator"/> class.
            /// </summary>
            /// <param name="iCollection">The i collection.</param>
            internal V2TaskTriggerEnumerator(V2.ITriggerCollection iCollection)
            {
                this.iEnum = iCollection.GetEnumerator();
            }

            #region IEnumerator<Trigger> Members

            /// <summary>
            /// Gets the element in the collection at the current position of the enumerator.
            /// </summary>
            /// <value></value>
            /// <returns>
            /// The element in the collection at the current position of the enumerator.
            /// </returns>
            public Trigger Current
            {
                get
                {
                    return Trigger.CreateTrigger((V2.ITrigger)this.iEnum.Current);
                }
            }

            #endregion

            #region IDisposable Members

            /// <summary>
            /// Releases all resources used by this class.
            /// </summary>
            public void Dispose()
            {
                this.iEnum = null;
            }

            #endregion

            #region IEnumerator Members

            /// <summary>
            /// Gets the element in the collection at the current position of the enumerator.
            /// </summary>
            /// <value></value>
            /// <returns>
            /// The element in the collection at the current position of the enumerator.
            /// </returns>
            object System.Collections.IEnumerator.Current
            {
                get { return this.Current; }
            }

            /// <summary>
            /// Advances the enumerator to the next element of the collection.
            /// </summary>
            /// <returns>
            /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
            /// </returns>
            /// <exception cref="T:System.InvalidOperationException">
            /// The collection was modified after the enumerator was created.
            /// </exception>
            public bool MoveNext()
            {
                return this.iEnum.MoveNext();
            }

            /// <summary>
            /// Sets the enumerator to its initial position, which is before the first element in the collection.
            /// </summary>
            /// <exception cref="T:System.InvalidOperationException">
            /// The collection was modified after the enumerator was created.
            /// </exception>
            public void Reset()
            {
                this.iEnum.Reset();
            }

            #endregion
        }

        #endregion
    }
}
